//
// Created by huxiufeng on 2018/6/28.
//

#include "CTest.h"
#include <glog/logging.h>
#include <event.h>
#include <gflags/gflags.h>
#include <arpa/inet.h>
#include <string>



DEFINE_string(localip, "127.0.0.1","local ip");
DEFINE_int32(localport, 23456, "local port");




static void do_accept(evutil_socket_t listener, short event, void *arg);
static void readcb(struct bufferevent *bev, void *ctx);
static void errorcb(struct bufferevent *bev, short error, void *ctx);

int main(int argc, char **argv) {

    google::InitGoogleLogging(argv[0]);
    FLAGS_stderrthreshold=google::INFO;
    FLAGS_colorlogtostderr=true;

    gflags::ParseCommandLineFlags(&argc, &argv, false);

    LOG(INFO)<<"localip: "<<FLAGS_localip;
    LOG(INFO)<<"localport: "<<FLAGS_localport;


    struct sockaddr_in sin;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(FLAGS_localip.c_str());
    sin.sin_port = htons(FLAGS_localport);

    evutil_socket_t listener;
    struct event_base *base;
    struct event *listener_event;


    base = event_base_new();
    if(!base) {
        LOG(ERROR) << "event_base_new error";
        return -1;
    }

    listener = socket(AF_INET, SOCK_STREAM, 0);
    evutil_make_socket_nonblocking(listener);


    if(bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        LOG(ERROR) << "bind error   "<< sin.sin_addr.s_addr << ":"<<sin.sin_port;
        return -1;
    }

    if(listen(listener, 16) < 0) {
        LOG(ERROR) <<"listen err";
        return -1;
    }

    listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void *)base);

    event_add(listener_event, NULL);
    event_base_dispatch(base);

    return 0;


    return 0;
}

static void do_accept(evutil_socket_t listener, short event, void *arg) {
    struct event_base *base = (struct event_base *)arg;
    struct sockaddr_storage ss;

    socklen_t slen = sizeof(ss);
    int fd = accept(listener, (struct sockaddr *)&ss, &slen);
    if(fd < 0) {
        LOG(ERROR) << "accept err , fd:"<<fd;
        return;
    } else if(fd > FD_SETSIZE) {
        LOG(ERROR) << "accept err , fd:"<<fd;
        close(fd);
        return;
    } else {
        struct bufferevent *bev;
        evutil_make_socket_nonblocking(fd);
        bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
        bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);
        bufferevent_enable(bev, EV_READ|EV_WRITE);
        return;
    }


    return;
}

static void readcb(struct bufferevent *bev, void *ctx) {
    struct evbuffer *input, *output;

    input = bufferevent_get_input(bev);
    output = bufferevent_get_output(bev);

    size_t inlen = evbuffer_get_length(input);
    LOG(INFO) << "input len :"<<inlen;

    size_t outlen = evbuffer_get_length(output);
    LOG(INFO) << "output len :"<<outlen;


    char *req_line;
    size_t len;

    while(true) {
        req_line = evbuffer_readln(input, &len, EVBUFFER_EOL_CRLF);
        if(NULL == req_line) {
            LOG(INFO) <<"no data left.";
            break;
        }
        LOG(INFO) << "Got data ["<<req_line<<']';

        std::string resp = "Response ok\r\n";
        evbuffer_add(output, resp.c_str(), strlen(resp.c_str()));


        free(req_line);
        req_line = NULL;
    }

    return;
}
static void errorcb(struct bufferevent *bev, short error, void *ctx) {
    if( error & BEV_EVENT_EOF) {
        LOG(INFO) << "connection closed.";
    } else if (error & BEV_EVENT_ERROR) {
        LOG(ERROR) << "connection error";
    } else if( error & BEV_EVENT_TIMEOUT) {
        LOG(ERROR) << "connectino time out.";
    }
    bufferevent_free(bev);
    return;

}